

/*-------------------------------------------------------------------------
 *
 * jsonpath_exec.c
 *	 Routines for SQL/JSON path execution.
 *
 * Jsonpath 在存储于 JsonPathExecContext 的全局上下文中执行，
 * 该上下文几乎传递给每个参与执行的函数。jsonpath 执行的入口点
 * 是 executeJsonPath() 函数，该函数初始化执行上下文，包括初始
 * JsonPathItem 和 JsonbValue，标志和用于计算过滤器中 @ 的栈。
 *
 * jsonpath 查询执行的结果是枚举 JsonPathExecResult，
 * 如果成功，则返回一系列 JsonbValue，写入 JsonValueList *found，
 * 该列表通过 jsonpath 项传递。当 found == NULL 时，我们处于
 * exists-query 中，我们只关心结果是否为空。在这种情况下，
 * 一旦找到第一个结果项，执行就停止，唯一的执行结果是 JsonPathExecResult。
 * JsonPathExecResult 的值如下：
 * - jperOk			-- 结果序列不为空
 * - jperNotFound	-- 结果序列为空
 * - jperError		-- 执行过程中发生错误
 *
 * Jsonpath 是递归执行的（见 executeItem()），从第一个路径项
 * 开始（这个路径项可能是单独评估的算术表达式）。在每一步中，从
 * 前一个路径项获取的单个 JsonbValue 被处理。处理的结果是一系列
 * JsonbValue（可能为空），这些值逐一传递给下一个路径项。当没有
 * 下一个路径项时，JsonbValue 被添加到 'found' 列表中。当 found == NULL，
 * 执行函数只是返回 jperOk（见 executeNextItem()）。
 *
 * 许多 jsonpath 操作要求在宽松模式下自动展开数组。
 * 因此，如果输入值是数组，则相应的操作不是在数组本身上处理，
 * 而是逐一处理所有成员。executeItemOptUnwrapTarget() 函数有 'unwrap' 
 * 参数，该参数指示是否需要展开数组。当 unwrap == true 时，数组的每个
 * 成员再次传递到 executeItemOptUnwrapTarget()，但 unwrap == false，
 * 以避免后续的数组展开。
 *
 * 所有布尔表达式（谓词）由 executeBoolItem() 函数评估，该函数
 * 返回三态值 JsonPathBool。当谓词执行过程中发生错误时，
 * 返回 jpbUnknown。根据标准，谓词只能在过滤器中使用。
 * 但是我们支持将它们用作 jsonpath 表达式。这帮助我们实现 @@ 操作符。
 * 在这种情况下，结果 JsonPathBool 被转换为 jsonb 布尔值或 null。
 *
 * 算术和布尔表达式是从表达式树的顶部向下递归评估的。
 * 因此，对于二元算术表达式，我们首先计算操作数。
 * 然后我们检查结果是否为数字单例列表，计算结果并将其传递给下一个路径项。
 *
 * Copyright (c) 2019-2022, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *	src/backend/utils/adt/jsonpath_exec.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "regex/regex.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/datetime.h"
#include "utils/datum.h"
#include "utils/float.h"
#include "utils/formatting.h"
#include "utils/guc.h"
#include "utils/json.h"
#include "utils/jsonpath.h"
#include "utils/timestamp.h"
#include "utils/varlena.h"

/*
 * 表示“基本对象”及其“id”以便进行 .keyvalue() 评估。
 */
typedef struct JsonBaseObjectInfo
{
	JsonbContainer *jbc;
	int			id;
} JsonBaseObjectInfo;

/*
 * jsonpath 执行的上下文。
 */
typedef struct JsonPathExecContext
{
	Jsonb	   *vars;			/* 要替换到 jsonpath 中的变量 */
	JsonbValue *root;			/* 用于 $ 评估 */
	JsonbValue *current;		/* 用于 @ 评估 */
	JsonBaseObjectInfo baseObject;	/* 用于 .keyvalue() 评估的 "基础对象" */
	int			lastGeneratedObjectId;	/* 用于 .keyvalue() 评估的 "id" 计数器 */
	int			innermostArraySize; /* 用于 LAST 数组索引评估 */
	bool		laxMode;		/* "lax" 模式为 true，"strict" 模式为 false */
	bool		ignoreStructuralErrors; /* 在 "true" 时，像缺少所需的 json 项或意外的 json 项类型这样的结构错误会被忽略 */
	bool		throwErrors;	/* 在 "false" 时，所有可抑制的错误都被抑制 */
	bool		useTz;
} JsonPathExecContext;

/* LIKE_REGEX 执行的上下文。 */
typedef struct JsonLikeRegexContext
{
	text	   *regex;
	int			cflags;
} JsonLikeRegexContext;

/* jsonpath 谓词评估的结果 */
typedef enum JsonPathBool
{
	jpbFalse = 0,
	jpbTrue = 1,
	jpbUnknown = 2
} JsonPathBool;

/* jsonpath 表达式评估的结果 */
typedef enum JsonPathExecResult
{
	jperOk = 0,
	jperNotFound = 1,
	jperError = 2
} JsonPathExecResult;

#define jperIsError(jper)			((jper) == jperError)

/*
 * 带有单值列表快捷方式的 jsonb 值列表。
 */
typedef struct JsonValueList
{
	JsonbValue *singleton;
	List	   *list;
} JsonValueList;

typedef struct JsonValueListIterator
{
	JsonbValue *value;
	List	   *list;
	ListCell   *next;
} JsonValueListIterator;

/* strict/lax 标志被分解为四个 [un]wrap/error 标志 */
#define jspStrictAbsenseOfErrors(cxt)	(!(cxt)->laxMode)
#define jspAutoUnwrap(cxt)				((cxt)->laxMode)
#define jspAutoWrap(cxt)				((cxt)->laxMode)
#define jspIgnoreStructuralErrors(cxt)	((cxt)->ignoreStructuralErrors)
#define jspThrowErrors(cxt)				((cxt)->throwErrors)

/* 便利宏：根据上下文返回或抛出错误 */
#define RETURN_ERROR(throw_error) \
do { \
	if (jspThrowErrors(fc_cxt)) \
		throw_error; \
	else \
		return jperError; \
} while (0)

typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
												   JsonbValue *larg,
												   JsonbValue *rarg,
												   void *param);
typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);

static JsonPathExecResult fc_executeJsonPath(JsonPath *fc_path, Jsonb *fc_vars,
										  Jsonb *fc_json, bool fc_throwErrors,
										  JsonValueList *fc_result, bool fc_useTz);
static JsonPathExecResult fc_executeItem(JsonPathExecContext *fc_cxt,
									  JsonPathItem *fc_jsp, JsonbValue *fc_jb, JsonValueList *fc_found);
static JsonPathExecResult fc_executeItemOptUnwrapTarget(JsonPathExecContext *fc_cxt,
													 JsonPathItem *fc_jsp, JsonbValue *fc_jb,
													 JsonValueList *fc_found, bool fc_unwrap);
static JsonPathExecResult fc_executeItemUnwrapTargetArray(JsonPathExecContext *fc_cxt,
													   JsonPathItem *fc_jsp, JsonbValue *fc_jb,
													   JsonValueList *fc_found, bool fc_unwrapElements);
static JsonPathExecResult fc_executeNextItem(JsonPathExecContext *fc_cxt,
										  JsonPathItem *fc_cur, JsonPathItem *fc_next,
										  JsonbValue *fc_v, JsonValueList *fc_found, bool fc_copy);
static JsonPathExecResult fc_executeItemOptUnwrapResult(JsonPathExecContext *fc_cxt, JsonPathItem *fc_jsp, JsonbValue *fc_jb,
													 bool fc_unwrap, JsonValueList *fc_found);
static JsonPathExecResult fc_executeItemOptUnwrapResultNoThrow(JsonPathExecContext *fc_cxt, JsonPathItem *fc_jsp,
															JsonbValue *fc_jb, bool fc_unwrap, JsonValueList *fc_found);
static JsonPathBool fc_executeBoolItem(JsonPathExecContext *fc_cxt,
									JsonPathItem *fc_jsp, JsonbValue *fc_jb, bool fc_canHaveNext);
static JsonPathBool fc_executeNestedBoolItem(JsonPathExecContext *fc_cxt,
										  JsonPathItem *fc_jsp, JsonbValue *fc_jb);
static JsonPathExecResult fc_executeAnyItem(JsonPathExecContext *fc_cxt,
										 JsonPathItem *fc_jsp, JsonbContainer *fc_jbc, JsonValueList *fc_found,
										 uint32 fc_level, uint32 fc_first, uint32 fc_last,
										 bool fc_ignoreStructuralErrors, bool fc_unwrapNext);
static JsonPathBool fc_executePredicate(JsonPathExecContext *fc_cxt,
									 JsonPathItem *fc_pred, JsonPathItem *fc_larg, JsonPathItem *fc_rarg,
									 JsonbValue *fc_jb, bool fc_unwrapRightArg,
									 JsonPathPredicateCallback fc_exec, void *fc_param);
static JsonPathExecResult fc_executeBinaryArithmExpr(JsonPathExecContext *fc_cxt,
												  JsonPathItem *fc_jsp, JsonbValue *fc_jb,
												  BinaryArithmFunc fc_func, JsonValueList *fc_found);
static JsonPathExecResult fc_executeUnaryArithmExpr(JsonPathExecContext *fc_cxt,
												 JsonPathItem *fc_jsp, JsonbValue *fc_jb, PGFunction fc_func,
												 JsonValueList *fc_found);
static JsonPathBool fc_executeStartsWith(JsonPathItem *fc_jsp,
									  JsonbValue *fc_whole, JsonbValue *fc_initial, void *fc_param);
static JsonPathBool fc_executeLikeRegex(JsonPathItem *fc_jsp, JsonbValue *fc_str,
									 JsonbValue *fc_rarg, void *fc_param);
static JsonPathExecResult fc_executeNumericItemMethod(JsonPathExecContext *fc_cxt,
												   JsonPathItem *fc_jsp, JsonbValue *fc_jb, bool fc_unwrap, PGFunction fc_func,
												   JsonValueList *fc_found);
static JsonPathExecResult fc_executeDateTimeMethod(JsonPathExecContext *fc_cxt, JsonPathItem *fc_jsp,
												JsonbValue *fc_jb, JsonValueList *fc_found);
static JsonPathExecResult fc_executeKeyValueMethod(JsonPathExecContext *fc_cxt,
												JsonPathItem *fc_jsp, JsonbValue *fc_jb, JsonValueList *fc_found);
static JsonPathExecResult fc_appendBoolResult(JsonPathExecContext *fc_cxt,
										   JsonPathItem *fc_jsp, JsonValueList *fc_found, JsonPathBool fc_res);
static void fc_getJsonPathItem(JsonPathExecContext *fc_cxt, JsonPathItem *fc_item,
							JsonbValue *fc_value);
static void fc_getJsonPathVariable(JsonPathExecContext *fc_cxt,
								JsonPathItem *fc_variable, Jsonb *fc_vars, JsonbValue *fc_value);
static int	fc_JsonbArraySize(JsonbValue *fc_jb);
static JsonPathBool fc_executeComparison(JsonPathItem *fc_cmp, JsonbValue *fc_lv,
									  JsonbValue *fc_rv, void *fc_p);
static JsonPathBool fc_compareItems(int32 fc_op, JsonbValue *fc_jb1, JsonbValue *fc_jb2,
								 bool fc_useTz);
static int	fc_compareNumeric(Numeric fc_a, Numeric fc_b);
static JsonbValue *fc_copyJsonbValue(JsonbValue *fc_src);
static JsonPathExecResult fc_getArrayIndex(JsonPathExecContext *fc_cxt,
										JsonPathItem *fc_jsp, JsonbValue *fc_jb, int32 *fc_index);
static JsonBaseObjectInfo fc_setBaseObject(JsonPathExecContext *fc_cxt,
										JsonbValue *fc_jbv, int32 fc_id);
static void fc_JsonValueListAppend(JsonValueList *fc_jvl, JsonbValue *fc_jbv);
static int	fc_JsonValueListLength(const JsonValueList *fc_jvl);
static bool fc_JsonValueListIsEmpty(JsonValueList *fc_jvl);
static JsonbValue *fc_JsonValueListHead(JsonValueList *fc_jvl);
static List *fc_JsonValueListGetList(JsonValueList *fc_jvl);
static void fc_JsonValueListInitIterator(const JsonValueList *fc_jvl,
									  JsonValueListIterator *fc_it);
static JsonbValue *fc_JsonValueListNext(const JsonValueList *fc_jvl,
									 JsonValueListIterator *fc_it);
static int	fc_JsonbType(JsonbValue *fc_jb);
static JsonbValue *fc_JsonbInitBinary(JsonbValue *fc_jbv, Jsonb *fc_jb);
static int	fc_JsonbType(JsonbValue *fc_jb);
static JsonbValue *fc_getScalar(JsonbValue *fc_scalar, enum jbvType fc_type);
static JsonbValue *fc_wrapItemsInArray(const JsonValueList *fc_items);
static int	fc_compareDatetime(Datum fc_val1, Oid fc_typid1, Datum fc_val2, Oid fc_typid2,
							bool fc_useTz, bool *fc_have_error);

/****************** JsonPath 执行器的用户界面 ********************/

/*
 * jsonb_path_exists
 *		如果 jsonpath 为指定的 jsonb 值返回至少一个项，则返回 true。
 *		此函数和 jsonb_path_match() 用于实现 @? 和 @@ 操作符，
 *		反过来这些操作符旨在具有索引支持。因此，尽量减少错误非常重要。
 *		关于此函数，这样的行为也匹配 SQL/JSON 的 JSON_EXISTS() 子句的行为。
 *		关于 jsonb_path_match()，此函数在 SQL/JSON 中没有类似的功能，
 *		所以我们自行定义其行为。
 */
static Datum fc_jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool fc_tz)
{
	Jsonb	   *fc_jb = PG_GETARG_JSONB_P(0);
	JsonPath   *fc_jp = PG_GETARG_JSONPATH_P(1);
	JsonPathExecResult fc_res;
	Jsonb	   *fc_vars = NULL;
	bool		fc_silent = true;

	if (PG_NARGS() == 4)
	{
		fc_vars = PG_GETARG_JSONB_P(2);
		fc_silent = PG_GETARG_BOOL(3);
	}

	fc_res = fc_executeJsonPath(fc_jp, fc_vars, fc_jb, !fc_silent, NULL, fc_tz);

	PG_FREE_IF_COPY(fc_jb, 0);
	PG_FREE_IF_COPY(fc_jp, 1);

	if (jperIsError(fc_res))
		PG_RETURN_NULL();

	PG_RETURN_BOOL(fc_res == jperOk);
}

Datum jsonb_path_exists(PG_FUNCTION_ARGS)
{
	return fc_jsonb_path_exists_internal(fcinfo, false);
}

Datum jsonb_path_exists_tz(PG_FUNCTION_ARGS)
{
	return fc_jsonb_path_exists_internal(fcinfo, true);
}

/*
 * jsonb_path_exists_opr
 *		运算符 "jsonb @? jsonpath" 的实现（jsonb_path_exists() 的 2 参数版本）。
 */
Datum jsonb_path_exists_opr(PG_FUNCTION_ARGS)
{
	/* 只需调用另一个 -- 它可以处理两种情况 */
	return fc_jsonb_path_exists_internal(fcinfo, false);
}

/*
 * jsonb_path_match
 *		为指定的 jsonb 值返回 jsonpath 谓词结果项。
 *		有关错误处理的详细信息，请参见 jsonb_path_exists() 注释。
 */
static Datum fc_jsonb_path_match_internal(FunctionCallInfo fcinfo, bool fc_tz)
{
	Jsonb	   *fc_jb = PG_GETARG_JSONB_P(0);
	JsonPath   *fc_jp = PG_GETARG_JSONPATH_P(1);
	JsonValueList fc_found = {0};
	Jsonb	   *fc_vars = NULL;
	bool		fc_silent = true;

	if (PG_NARGS() == 4)
	{
		fc_vars = PG_GETARG_JSONB_P(2);
		fc_silent = PG_GETARG_BOOL(3);
	}

	(void) fc_executeJsonPath(fc_jp, fc_vars, fc_jb, !fc_silent, &fc_found, fc_tz);

	PG_FREE_IF_COPY(fc_jb, 0);
	PG_FREE_IF_COPY(fc_jp, 1);

	if (fc_JsonValueListLength(&fc_found) == 1)
	{
		JsonbValue *fc_jbv = fc_JsonValueListHead(&fc_found);

		if (fc_jbv->type == jbvBool)
			PG_RETURN_BOOL(fc_jbv->val.boolean);

		if (fc_jbv->type == jbvNull)
			PG_RETURN_NULL();
	}

	if (!fc_silent)
		ereport(ERROR,
				(errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
				 errmsg("single boolean result is expected")));

	PG_RETURN_NULL();
}

Datum jsonb_path_match(PG_FUNCTION_ARGS)
{
	return fc_jsonb_path_match_internal(fcinfo, false);
}

Datum jsonb_path_match_tz(PG_FUNCTION_ARGS)
{
	return fc_jsonb_path_match_internal(fcinfo, true);
}

/*
 * jsonb_path_match_opr
 *		运算符 "jsonb @@ jsonpath" 的实现（jsonb_path_match() 的 2 参数版本）。
 */
Datum jsonb_path_match_opr(PG_FUNCTION_ARGS)
{
	/* 只需调用另一个 -- 它可以处理两种情况 */
	return fc_jsonb_path_match_internal(fcinfo, false);
}

/*
 * jsonb_path_query
 *		为给定的 jsonb 文档执行 jsonpath 并将结果作为行集返回。
 */
static Datum fc_jsonb_path_query_internal(FunctionCallInfo fcinfo, bool fc_tz)
{
	FuncCallContext *fc_funcctx;
	List	   *fc_found;
	JsonbValue *fc_v;
	ListCell   *fc_c;

	if (SRF_IS_FIRSTCALL())
	{
		JsonPath   *fc_jp;
		Jsonb	   *fc_jb;
		MemoryContext fc_oldcontext;
		Jsonb	   *fc_vars;
		bool		fc_silent;
		JsonValueList fc_found = {0};

		fc_funcctx = SRF_FIRSTCALL_INIT();
		fc_oldcontext = MemoryContextSwitchTo(fc_funcctx->multi_call_memory_ctx);

		fc_jb = PG_GETARG_JSONB_P_COPY(0);
		fc_jp = PG_GETARG_JSONPATH_P_COPY(1);
		fc_vars = PG_GETARG_JSONB_P_COPY(2);
		fc_silent = PG_GETARG_BOOL(3);

		(void) fc_executeJsonPath(fc_jp, fc_vars, fc_jb, !fc_silent, &fc_found, fc_tz);

		fc_funcctx->user_fctx = fc_JsonValueListGetList(&fc_found);

		MemoryContextSwitchTo(fc_oldcontext);
	}

	fc_funcctx = SRF_PERCALL_SETUP();
	fc_found = fc_funcctx->user_fctx;

	fc_c = list_head(fc_found);

	if (fc_c == NULL)
		SRF_RETURN_DONE(fc_funcctx);

	fc_v = lfirst(fc_c);
	fc_funcctx->user_fctx = list_delete_first(fc_found);

	SRF_RETURN_NEXT(fc_funcctx, JsonbPGetDatum(JsonbValueToJsonb(fc_v)));
}

Datum jsonb_path_query(PG_FUNCTION_ARGS)
{
	return fc_jsonb_path_query_internal(fcinfo, false);
}

Datum jsonb_path_query_tz(PG_FUNCTION_ARGS)
{
	return fc_jsonb_path_query_internal(fcinfo, true);
}

/*
 * jsonb_path_query_array
 *		为给定的 jsonb 文档执行 jsonpath 并将结果作为 jsonb 数组返回。
 */
static Datum fc_jsonb_path_query_array_internal(FunctionCallInfo fcinfo, bool fc_tz)
{
	Jsonb	   *fc_jb = PG_GETARG_JSONB_P(0);
	JsonPath   *fc_jp = PG_GETARG_JSONPATH_P(1);
	JsonValueList fc_found = {0};
	Jsonb	   *fc_vars = PG_GETARG_JSONB_P(2);
	bool		fc_silent = PG_GETARG_BOOL(3);

	(void) fc_executeJsonPath(fc_jp, fc_vars, fc_jb, !fc_silent, &fc_found, fc_tz);

	PG_RETURN_JSONB_P(JsonbValueToJsonb(fc_wrapItemsInArray(&fc_found)));
}

Datum jsonb_path_query_array(PG_FUNCTION_ARGS)
{
	return fc_jsonb_path_query_array_internal(fcinfo, false);
}

Datum jsonb_path_query_array_tz(PG_FUNCTION_ARGS)
{
	return fc_jsonb_path_query_array_internal(fcinfo, true);
}

/*
 * jsonb_path_query_first
 *		为给定的 jsonb 文档执行 jsonpath 并返回第一个结果项。
 *		如果没有项，则返回 NULL。
 */
static Datum fc_jsonb_path_query_first_internal(FunctionCallInfo fcinfo, bool fc_tz)
{
	Jsonb	   *fc_jb = PG_GETARG_JSONB_P(0);
	JsonPath   *fc_jp = PG_GETARG_JSONPATH_P(1);
	JsonValueList fc_found = {0};
	Jsonb	   *fc_vars = PG_GETARG_JSONB_P(2);
	bool		fc_silent = PG_GETARG_BOOL(3);

	(void) fc_executeJsonPath(fc_jp, fc_vars, fc_jb, !fc_silent, &fc_found, fc_tz);

	if (fc_JsonValueListLength(&fc_found) >= 1)
		PG_RETURN_JSONB_P(JsonbValueToJsonb(fc_JsonValueListHead(&fc_found)));
	else
		PG_RETURN_NULL();
}

Datum jsonb_path_query_first(PG_FUNCTION_ARGS)
{
	return fc_jsonb_path_query_first_internal(fcinfo, false);
}

Datum jsonb_path_query_first_tz(PG_FUNCTION_ARGS)
{
	return fc_jsonb_path_query_first_internal(fcinfo, true);
}

/******************** JsonPath 的执行函数 **************************/

/*
 * jsonpath 执行器的接口
 *
 * 'path' - 要执行的 jsonpath
 * 'vars' - 要替换到 jsonpath 的变量
 * 'json' - jsonpath 评估的目标文档
 * 'throwErrors' - 我们是否应该抛出可抑制的错误
 * 'result' - 存储结果项的列表
 *
 * 如果在处理过程中发生可恢复的错误则返回错误，或在没有错误时返回 NULL。
 *
 * 请注意，jsonb 和 jsonpath 值在工作过程中应可用且未烤制，
 * 因为 JsonPathItem、JsonbValue 和结果项可能指向输入值。
 * 如果调用者只需要检查文档是否匹配 jsonpath，
 * 则不提供结果参数。在这种情况下，执行器在第一个正结果上工作，
 * 并且在可能的情况不检查其余部分。
 * 否则，它会尝试找到所有满足条件的结果项。
 */
static JsonPathExecResult fc_executeJsonPath(JsonPath *fc_path, Jsonb *fc_vars, Jsonb *fc_json, bool fc_throwErrors,
				JsonValueList *fc_result, bool fc_useTz)
{
	JsonPathExecContext fc_cxt;
	JsonPathExecResult fc_res;
	JsonPathItem fc_jsp;
	JsonbValue	fc_jbv;

	jspInit(&fc_jsp, fc_path);

	if (!JsonbExtractScalar(&fc_json->root, &fc_jbv))
		fc_JsonbInitBinary(&fc_jbv, fc_json);

	if (fc_vars && !JsonContainerIsObject(&fc_vars->root))
	{
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("\"vars\" argument is not an object"),
				 errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object.")));
	}

	fc_cxt.vars = fc_vars;
	fc_cxt.laxMode = (fc_path->header & JSONPATH_LAX) != 0;
	fc_cxt.ignoreStructuralErrors = fc_cxt.laxMode;
	fc_cxt.root = &fc_jbv;
	fc_cxt.current = &fc_jbv;
	fc_cxt.baseObject.jbc = NULL;
	fc_cxt.baseObject.id = 0;
	fc_cxt.lastGeneratedObjectId = fc_vars ? 2 : 1;
	fc_cxt.innermostArraySize = -1;
	fc_cxt.throwErrors = fc_throwErrors;
	fc_cxt.useTz = fc_useTz;

	if (jspStrictAbsenseOfErrors(&fc_cxt) && !fc_result)
	{
		/*
		 * 在严格模式下，我们必须获得完整的值列表以检查是否没有错误。
		 */
		JsonValueList fc_vals = {0};

		fc_res = fc_executeItem(&fc_cxt, &fc_jsp, &fc_jbv, &fc_vals);

		if (jperIsError(fc_res))
			return fc_res;

		return fc_JsonValueListIsEmpty(&fc_vals) ? jperNotFound : jperOk;
	}

	fc_res = fc_executeItem(&fc_cxt, &fc_jsp, &fc_jbv, fc_result);

	Assert(!fc_throwErrors || !jperIsError(fc_res));

	return fc_res;
}

/*
 * 在宽松模式下自动解包当前项的 jsonpath 执行。
 */
static JsonPathExecResult fc_executeItem(JsonPathExecContext *fc_cxt, JsonPathItem *fc_jsp,
			JsonbValue *fc_jb, JsonValueList *fc_found)
{
	return fc_executeItemOptUnwrapTarget(fc_cxt, fc_jsp, fc_jb, fc_found, jspAutoUnwrap(fc_cxt));
}

/*
 * 主要的 jsonpath 执行器函数：遍历 jsonpath 结构，查找
 * jsonb 的相关部分并在其上评估表达式。
 * 当 'unwrap' 为 true 时，如果当前 SQL/JSON 项是数组，则将其解包。
 */
static JsonPathExecResult fc_executeItemOptUnwrapTarget(JsonPathExecContext *fc_cxt, JsonPathItem *fc_jsp,
						   JsonbValue *fc_jb, JsonValueList *fc_found, bool fc_unwrap)
{
	JsonPathItem fc_elem;
	JsonPathExecResult fc_res = jperNotFound;
	JsonBaseObjectInfo fc_baseObject;

	check_stack_depth();
	CHECK_FOR_INTERRUPTS();

	switch (fc_jsp->type)
	{
			/* 所有布尔项类型： */
		case jpiAnd:
		case jpiOr:
		case jpiNot:
		case jpiIsUnknown:
		case jpiEqual:
		case jpiNotEqual:
		case jpiLess:
		case jpiGreater:
		case jpiLessOrEqual:
		case jpiGreaterOrEqual:
		case jpiExists:
		case jpiStartsWith:
		case jpiLikeRegex:
			{
				JsonPathBool fc_st = fc_executeBoolItem(fc_cxt, fc_jsp, fc_jb, true);

				fc_res = fc_appendBoolResult(fc_cxt, fc_jsp, fc_found, fc_st);
				break;
			}

		case jpiKey:
			if (fc_JsonbType(fc_jb) == jbvObject)
			{
				JsonbValue *fc_v;
				JsonbValue	fc_key;

				fc_key.type = jbvString;
				fc_key.val.string.val = jspGetString(fc_jsp, &fc_key.val.string.len);

				fc_v = findJsonbValueFromContainer(fc_jb->val.binary.data,
												JB_FOBJECT, &fc_key);

				if (fc_v != NULL)
				{
					fc_res = fc_executeNextItem(fc_cxt, fc_jsp, NULL,
										  fc_v, fc_found, false);

					/* 如果未添加到找到列表，请释放值 */
					if (jspHasNext(fc_jsp) || !fc_found)
						pfree(fc_v);
				}
				else if (!jspIgnoreStructuralErrors(fc_cxt))
				{
					Assert(fc_found);

					if (!jspThrowErrors(fc_cxt))
						return jperError;

					ereport(ERROR,
							(errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND), \
							 errmsg("JSON object does not contain key \"%s\"",
									pnstrdup(fc_key.val.string.val,
											 fc_key.val.string.len))));
				}
			}
			else if (fc_unwrap && fc_JsonbType(fc_jb) == jbvArray)
				return fc_executeItemUnwrapTargetArray(fc_cxt, fc_jsp, fc_jb, fc_found, false);
			else if (!jspIgnoreStructuralErrors(fc_cxt))
			{
				Assert(fc_found);
				RETURN_ERROR(ereport(ERROR,
									 (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND),
									  errmsg("jsonpath member accessor can only be applied to an object"))));
			}
			break;

		case jpiRoot:
			fc_jb = fc_cxt->root;
			fc_baseObject = fc_setBaseObject(fc_cxt, fc_jb, 0);
			fc_res = fc_executeNextItem(fc_cxt, fc_jsp, NULL, fc_jb, fc_found, true);
			fc_cxt->baseObject = fc_baseObject;
			break;

		case jpiCurrent:
			fc_res = fc_executeNextItem(fc_cxt, fc_jsp, NULL, fc_cxt->current,
								  fc_found, true);
			break;

		case jpiAnyArray:
			if (fc_JsonbType(fc_jb) == jbvArray)
			{
				bool		fc_hasNext = jspGetNext(fc_jsp, &fc_elem);

				fc_res = fc_executeItemUnwrapTargetArray(fc_cxt, fc_hasNext ? &fc_elem : NULL,
												   fc_jb, fc_found, jspAutoUnwrap(fc_cxt));
			}
			else if (jspAutoWrap(fc_cxt))
				fc_res = fc_executeNextItem(fc_cxt, fc_jsp, NULL, fc_jb, fc_found, true);
			else if (!jspIgnoreStructuralErrors(fc_cxt))
				RETURN_ERROR(ereport(ERROR,
									 (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
									  errmsg("jsonpath wildcard array accessor can only be applied to an array"))));
			break;

		case jpiIndexArray:
			if (fc_JsonbType(fc_jb) == jbvArray || jspAutoWrap(fc_cxt))
			{
				int			fc_innermostArraySize = fc_cxt->innermostArraySize;
				int			fc_i;
				int			fc_size = fc_JsonbArraySize(fc_jb);
				bool		fc_singleton = fc_size < 0;
				bool		fc_hasNext = jspGetNext(fc_jsp, &fc_elem);

				if (fc_singleton)
					fc_size = 1;

				fc_cxt->innermostArraySize = fc_size; /* 用于最后评估 */

				for (fc_i = 0; fc_i < fc_jsp->content.array.nelems; fc_i++)
				{
					JsonPathItem fc_from;
					JsonPathItem fc_to;
					int32		fc_index;
					int32		fc_index_from;
					int32		fc_index_to;
					bool		fc_range = jspGetArraySubscript(fc_jsp, &fc_from,
															 &fc_to, fc_i);

					fc_res = fc_getArrayIndex(fc_cxt, &fc_from, fc_jb, &fc_index_from);

					if (jperIsError(fc_res))
						break;

					if (fc_range)
					{
						fc_res = fc_getArrayIndex(fc_cxt, &fc_to, fc_jb, &fc_index_to);

						if (jperIsError(fc_res))
							break;
					}
					else
						fc_index_to = fc_index_from;

					if (!jspIgnoreStructuralErrors(fc_cxt) &&
						(fc_index_from < 0 ||
						 fc_index_from > fc_index_to ||
						 fc_index_to >= fc_size))
						RETURN_ERROR(ereport(ERROR,
											 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
											  errmsg("jsonpath array subscript is out of bounds"))));

					if (fc_index_from < 0)
						fc_index_from = 0;

					if (fc_index_to >= fc_size)
						fc_index_to = fc_size - 1;

					fc_res = jperNotFound;

					for (fc_index = fc_index_from; fc_index <= fc_index_to; fc_index++)
					{
						JsonbValue *fc_v;
						bool		fc_copy;

						if (fc_singleton)
						{
							fc_v = fc_jb;
							fc_copy = true;
						}
						else
						{
							fc_v = getIthJsonbValueFromContainer(fc_jb->val.binary.data,
															  (uint32) fc_index);

							if (fc_v == NULL)
								continue;

							fc_copy = false;
						}

						if (!fc_hasNext && !fc_found)
							return jperOk;

						fc_res = fc_executeNextItem(fc_cxt, fc_jsp, &fc_elem, fc_v, fc_found,
											  fc_copy);

						if (jperIsError(fc_res))
							break;

						if (fc_res == jperOk && !fc_found)
							break;
					}

					if (jperIsError(fc_res))
						break;

					if (fc_res == jperOk && !fc_found)
						break;
				}

				fc_cxt->innermostArraySize = fc_innermostArraySize;
			}
			else if (!jspIgnoreStructuralErrors(fc_cxt))
			{
				RETURN_ERROR(ereport(ERROR,
									 (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
									  errmsg("jsonpath array accessor can only be applied to an array"))));
			}
			break;

		case jpiLast:
			{
				JsonbValue	fc_tmpjbv;
				JsonbValue *fc_lastjbv;
				int			fc_last;
				bool		fc_hasNext = jspGetNext(fc_jsp, &fc_elem);

				if (fc_cxt->innermostArraySize < 0)
					elog(ERROR, "evaluating jsonpath LAST outside of array subscript");

				if (!fc_hasNext && !fc_found)
				{
					fc_res = jperOk;
					break;
				}

				fc_last = fc_cxt->innermostArraySize - 1;

				fc_lastjbv = fc_hasNext ? &fc_tmpjbv : palloc(sizeof(*fc_lastjbv));

				fc_lastjbv->type = jbvNumeric;
				fc_lastjbv->val.numeric = int64_to_numeric(fc_last);

				fc_res = fc_executeNextItem(fc_cxt, fc_jsp, &fc_elem,
									  fc_lastjbv, fc_found, fc_hasNext);
			}
			break;

		case jpiAnyKey:
			if (fc_JsonbType(fc_jb) == jbvObject)
			{
				bool		fc_hasNext = jspGetNext(fc_jsp, &fc_elem);

				if (fc_jb->type != jbvBinary)
					elog(ERROR, "invalid jsonb object type: %d", fc_jb->type);

				return fc_executeAnyItem
					(fc_cxt, fc_hasNext ? &fc_elem : NULL,
					 fc_jb->val.binary.data, fc_found, 1, 1, 1,
					 false, jspAutoUnwrap(fc_cxt));
			}
			else if (fc_unwrap && fc_JsonbType(fc_jb) == jbvArray)
				return fc_executeItemUnwrapTargetArray(fc_cxt, fc_jsp, fc_jb, fc_found, false);
			else if (!jspIgnoreStructuralErrors(fc_cxt))
			{
				Assert(fc_found);
				RETURN_ERROR(ereport(ERROR,
									 (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
									  errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
			}
			break;

		case jpiAdd:
			return fc_executeBinaryArithmExpr(fc_cxt, fc_jsp, fc_jb,
										   numeric_add_opt_error, fc_found);

		case jpiSub:
			return fc_executeBinaryArithmExpr(fc_cxt, fc_jsp, fc_jb,
										   numeric_sub_opt_error, fc_found);

		case jpiMul:
			return fc_executeBinaryArithmExpr(fc_cxt, fc_jsp, fc_jb,
										   numeric_mul_opt_error, fc_found);

		case jpiDiv:
			return fc_executeBinaryArithmExpr(fc_cxt, fc_jsp, fc_jb,
										   numeric_div_opt_error, fc_found);

		case jpiMod:
			return fc_executeBinaryArithmExpr(fc_cxt, fc_jsp, fc_jb,
										   numeric_mod_opt_error, fc_found);

		case jpiPlus:
			return fc_executeUnaryArithmExpr(fc_cxt, fc_jsp, fc_jb, NULL, fc_found);

		case jpiMinus:
			return fc_executeUnaryArithmExpr(fc_cxt, fc_jsp, fc_jb, numeric_uminus,
										  fc_found);

		case jpiFilter:
			{
				JsonPathBool fc_st;

				if (fc_unwrap && fc_JsonbType(fc_jb) == jbvArray)
					return fc_executeItemUnwrapTargetArray(fc_cxt, fc_jsp, fc_jb, fc_found,
														false);

				jspGetArg(fc_jsp, &fc_elem);
				fc_st = fc_executeNestedBoolItem(fc_cxt, &fc_elem, fc_jb);
				if (fc_st != jpbTrue)
					fc_res = jperNotFound;
				else
					fc_res = fc_executeNextItem(fc_cxt, fc_jsp, NULL,
										  fc_jb, fc_found, true);
				break;
			}

		case jpiAny:
			{
				bool		fc_hasNext = jspGetNext(fc_jsp, &fc_elem);

				/* 第一次尝试不经过任何中间步骤 */
				if (fc_jsp->content.anybounds.first == 0)
				{
					bool		fc_savedIgnoreStructuralErrors;

					fc_savedIgnoreStructuralErrors = fc_cxt->ignoreStructuralErrors;
					fc_cxt->ignoreStructuralErrors = true;
					fc_res = fc_executeNextItem(fc_cxt, fc_jsp, &fc_elem,
										  fc_jb, fc_found, true);
					fc_cxt->ignoreStructuralErrors = fc_savedIgnoreStructuralErrors;

					if (fc_res == jperOk && !fc_found)
						break;
				}

				if (fc_jb->type == jbvBinary)
					fc_res = fc_executeAnyItem
						(fc_cxt, fc_hasNext ? &fc_elem : NULL,
						 fc_jb->val.binary.data, fc_found,
						 1,
						 fc_jsp->content.anybounds.first,
						 fc_jsp->content.anybounds.last,
						 true, jspAutoUnwrap(fc_cxt));
				break;
			}

		case jpiNull:
		case jpiBool:
		case jpiNumeric:
		case jpiString:
		case jpiVariable:
			{
				JsonbValue	fc_vbuf;
				JsonbValue *fc_v;
				bool		fc_hasNext = jspGetNext(fc_jsp, &fc_elem);

				if (!fc_hasNext && !fc_found && fc_jsp->type != jpiVariable)
				{
					/* 
					 * 跳过评估，但变量除外。我们必须
					 * 对缺失的变量触发错误。
					 */
					fc_res = jperOk;
					break;
				}

				fc_v = fc_hasNext ? &fc_vbuf : palloc(sizeof(*fc_v));

				fc_baseObject = fc_cxt->baseObject;
				fc_getJsonPathItem(fc_cxt, fc_jsp, fc_v);

				fc_res = fc_executeNextItem(fc_cxt, fc_jsp, &fc_elem,
									  fc_v, fc_found, fc_hasNext);
				fc_cxt->baseObject = fc_baseObject;
			}
			break;

		case jpiType:
			{
				JsonbValue *fc_jbv = palloc(sizeof(*fc_jbv));

				fc_jbv->type = jbvString;
				fc_jbv->val.string.val = pstrdup(JsonbTypeName(fc_jb));
				fc_jbv->val.string.len = strlen(fc_jbv->val.string.val);

				fc_res = fc_executeNextItem(fc_cxt, fc_jsp, NULL, fc_jbv,
									  fc_found, false);
			}
			break;

		case jpiSize:
			{
				int			fc_size = fc_JsonbArraySize(fc_jb);

				if (fc_size < 0)
				{
					if (!jspAutoWrap(fc_cxt))
					{
						if (!jspIgnoreStructuralErrors(fc_cxt))
							RETURN_ERROR(ereport(ERROR,
												 (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
												  errmsg("jsonpath item method .%s() can only be applied to an array",
														 jspOperationName(fc_jsp->type)))));
						break;
					}

					fc_size = 1;
				}

				fc_jb = palloc(sizeof(*fc_jb));

				fc_jb->type = jbvNumeric;
				fc_jb->val.numeric = int64_to_numeric(fc_size);

				fc_res = fc_executeNextItem(fc_cxt, fc_jsp, NULL, fc_jb, fc_found, false);
			}
			break;

		case jpiAbs:
			return fc_executeNumericItemMethod(fc_cxt, fc_jsp, fc_jb, fc_unwrap, numeric_abs,
											fc_found);

		case jpiFloor:
			return fc_executeNumericItemMethod(fc_cxt, fc_jsp, fc_jb, fc_unwrap, numeric_floor,
											fc_found);

		case jpiCeiling:
			return fc_executeNumericItemMethod(fc_cxt, fc_jsp, fc_jb, fc_unwrap, numeric_ceil,
											fc_found);

		case jpiDouble:
			{
				JsonbValue	fc_jbv;

				if (fc_unwrap && fc_JsonbType(fc_jb) == jbvArray)
					return fc_executeItemUnwrapTargetArray(fc_cxt, fc_jsp, fc_jb, fc_found,
														false);

				if (fc_jb->type == jbvNumeric)
				{
					char	   *fc_tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
																		  NumericGetDatum(fc_jb->val.numeric)));
					double		fc_val;
					bool		fc_have_error = false;

					fc_val = float8in_internal_opt_error(fc_tmp,
													  NULL,
													  "double precision",
													  fc_tmp,
													  &fc_have_error);

					if (fc_have_error || isinf(fc_val) || isnan(fc_val))
						RETURN_ERROR(ereport(ERROR,
											 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
											  errmsg("numeric argument of jsonpath item method .%s() is out of range for type double precision",
													 jspOperationName(fc_jsp->type)))));
					fc_res = jperOk;
				}
				else if (fc_jb->type == jbvString)
				{
					/* 将字符串转换为双精度 */
					double		fc_val;
					char	   *fc_tmp = pnstrdup(fc_jb->val.string.val,
											   fc_jb->val.string.len);
					bool		fc_have_error = false;

					fc_val = float8in_internal_opt_error(fc_tmp,
													  NULL,
													  "double precision",
													  fc_tmp,
													  &fc_have_error);

					if (fc_have_error || isinf(fc_val) || isnan(fc_val))
						RETURN_ERROR(ereport(ERROR,
											 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
											  errmsg("string argument of jsonpath item method .%s() is not a valid representation of a double precision number",
													 jspOperationName(fc_jsp->type)))));

					fc_jb = &fc_jbv;
					fc_jb->type = jbvNumeric;
					fc_jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
																		  Float8GetDatum(fc_val)));
					fc_res = jperOk;
				}

				if (fc_res == jperNotFound)
					RETURN_ERROR(ereport(ERROR,
										 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
										  errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
												 jspOperationName(fc_jsp->type)))));

				fc_res = fc_executeNextItem(fc_cxt, fc_jsp, NULL, fc_jb, fc_found, true);
			}
			break;

		case jpiDatetime:
			if (fc_unwrap && fc_JsonbType(fc_jb) == jbvArray)
				return fc_executeItemUnwrapTargetArray(fc_cxt, fc_jsp, fc_jb, fc_found, false);

			return fc_executeDateTimeMethod(fc_cxt, fc_jsp, fc_jb, fc_found);

		case jpiKeyValue:
			if (fc_unwrap && fc_JsonbType(fc_jb) == jbvArray)
				return fc_executeItemUnwrapTargetArray(fc_cxt, fc_jsp, fc_jb, fc_found, false);

			return fc_executeKeyValueMethod(fc_cxt, fc_jsp, fc_jb, fc_found);

		default:
			elog(ERROR, "unrecognized jsonpath item type: %d", fc_jsp->type);
	}

	return fc_res;
}

/* 
 * 解包当前数组项并对其每个元素执行 jsonpath。
 */
static JsonPathExecResult fc_executeItemUnwrapTargetArray(JsonPathExecContext *fc_cxt, JsonPathItem *fc_jsp,
							 JsonbValue *fc_jb, JsonValueList *fc_found,
							 bool fc_unwrapElements)
{
	if (fc_jb->type != jbvBinary)
	{
		Assert(fc_jb->type != jbvArray);
		elog(ERROR, "invalid jsonb array value type: %d", fc_jb->type);
	}

	return fc_executeAnyItem
		(fc_cxt, fc_jsp, fc_jb->val.binary.data, fc_found, 1, 1, 1,
		 false, fc_unwrapElements);
}

/* 
 * 如果存在下一个 jsonpath 项，则执行。否则，如果提供，
 * 将“v”放入“找到”的列表中。
 */
static JsonPathExecResult fc_executeNextItem(JsonPathExecContext *fc_cxt,
				JsonPathItem *fc_cur, JsonPathItem *fc_next,
				JsonbValue *fc_v, JsonValueList *fc_found, bool fc_copy)
{
	JsonPathItem fc_elem;
	bool		fc_hasNext;

	if (!fc_cur)
		fc_hasNext = fc_next != NULL;
	else if (fc_next)
		fc_hasNext = jspHasNext(fc_cur);
	else
	{
		fc_next = &fc_elem;
		fc_hasNext = jspGetNext(fc_cur, fc_next);
	}

	if (fc_hasNext)
		return fc_executeItem(fc_cxt, fc_next, fc_v, fc_found);

	if (fc_found)
		fc_JsonValueListAppend(fc_found, fc_copy ? fc_copyJsonbValue(fc_v) : fc_v);

	return jperOk;
}

/* 
 * 与 executeItem() 相同，但当 "unwrap == true" 时，在宽松模式下自动解包
 * 每个数组项。
 */
static JsonPathExecResult fc_executeItemOptUnwrapResult(JsonPathExecContext *fc_cxt, JsonPathItem *fc_jsp,
						   JsonbValue *fc_jb, bool fc_unwrap,
						   JsonValueList *fc_found)
{
	if (fc_unwrap && jspAutoUnwrap(fc_cxt))
	{
		JsonValueList fc_seq = {0};
		JsonValueListIterator fc_it;
		JsonPathExecResult fc_res = fc_executeItem(fc_cxt, fc_jsp, fc_jb, &fc_seq);
		JsonbValue *fc_item;

		if (jperIsError(fc_res))
			return fc_res;

		fc_JsonValueListInitIterator(&fc_seq, &fc_it);
		while ((fc_item = fc_JsonValueListNext(&fc_seq, &fc_it)))
		{
			Assert(fc_item->type != jbvArray);

			if (fc_JsonbType(fc_item) == jbvArray)
				fc_executeItemUnwrapTargetArray(fc_cxt, NULL, fc_item, fc_found, false);
			else
				fc_JsonValueListAppend(fc_found, fc_item);
		}

		return jperOk;
	}

	return fc_executeItem(fc_cxt, fc_jsp, fc_jb, fc_found);
}

/* 
 * 与 executeItemOptUnwrapResult() 相同，但抑制错误。
 */
static JsonPathExecResult fc_executeItemOptUnwrapResultNoThrow(JsonPathExecContext *fc_cxt,
								  JsonPathItem *fc_jsp,
								  JsonbValue *fc_jb, bool fc_unwrap,
								  JsonValueList *fc_found)
{
	JsonPathExecResult fc_res;
	bool		fc_throwErrors = fc_cxt->throwErrors;

	fc_cxt->throwErrors = false;
	fc_res = fc_executeItemOptUnwrapResult(fc_cxt, fc_jsp, fc_jb, fc_unwrap, fc_found);
	fc_cxt->throwErrors = fc_throwErrors;

	return fc_res;
}

/* 执行布尔值 jsonpath 表达式。 */
static JsonPathBool fc_executeBoolItem(JsonPathExecContext *fc_cxt, JsonPathItem *fc_jsp,
				JsonbValue *fc_jb, bool fc_canHaveNext)
{
	JsonPathItem fc_larg;
	JsonPathItem fc_rarg;
	JsonPathBool fc_res;
	JsonPathBool fc_res2;

	/* 由于这个函数递归，可能会导致栈溢出 */
	check_stack_depth();

	if (!fc_canHaveNext && jspHasNext(fc_jsp))
		elog(ERROR, "boolean jsonpath item cannot have next item");

	switch (fc_jsp->type)
	{
		case jpiAnd:
			jspGetLeftArg(fc_jsp, &fc_larg);
			fc_res = fc_executeBoolItem(fc_cxt, &fc_larg, fc_jb, false);

			if (fc_res == jpbFalse)
				return jpbFalse;

			/* 
			 * SQL/JSON 说我们应该在 jperError 的情况下检查第二个参数
			 */

			jspGetRightArg(fc_jsp, &fc_rarg);
			fc_res2 = fc_executeBoolItem(fc_cxt, &fc_rarg, fc_jb, false);

			return fc_res2 == jpbTrue ? fc_res : fc_res2;

		case jpiOr:
			jspGetLeftArg(fc_jsp, &fc_larg);
			fc_res = fc_executeBoolItem(fc_cxt, &fc_larg, fc_jb, false);

			if (fc_res == jpbTrue)
				return jpbTrue;

			jspGetRightArg(fc_jsp, &fc_rarg);
			fc_res2 = fc_executeBoolItem(fc_cxt, &fc_rarg, fc_jb, false);

			return fc_res2 == jpbFalse ? fc_res : fc_res2;

		case jpiNot:
			jspGetArg(fc_jsp, &fc_larg);

			fc_res = fc_executeBoolItem(fc_cxt, &fc_larg, fc_jb, false);

			if (fc_res == jpbUnknown)
				return jpbUnknown;

			return fc_res == jpbTrue ? jpbFalse : jpbTrue;

		case jpiIsUnknown:
			jspGetArg(fc_jsp, &fc_larg);
			fc_res = fc_executeBoolItem(fc_cxt, &fc_larg, fc_jb, false);
			return fc_res == jpbUnknown ? jpbTrue : jpbFalse;

		case jpiEqual:
		case jpiNotEqual:
		case jpiLess:
		case jpiGreater:
		case jpiLessOrEqual:
		case jpiGreaterOrEqual:
			jspGetLeftArg(fc_jsp, &fc_larg);
			jspGetRightArg(fc_jsp, &fc_rarg);
			return fc_executePredicate(fc_cxt, fc_jsp, &fc_larg, &fc_rarg, fc_jb, true,
									fc_executeComparison, fc_cxt);

		case jpiStartsWith:		/* '整个以初始开头' */
			jspGetLeftArg(fc_jsp, &fc_larg);	/* 'whole' */
			jspGetRightArg(fc_jsp, &fc_rarg); /* '初始' */
			return fc_executePredicate(fc_cxt, fc_jsp, &fc_larg, &fc_rarg, fc_jb, false,
									fc_executeStartsWith, NULL);

		case jpiLikeRegex:		/* 'expr LIKE_REGEX 模式 FLAGS 标志' */
			{
				/* 
				 * 'expr' 是返回序列的表达式。 'pattern' 是一个
				 * 正则表达式字符串。 SQL/JSON 标准要求使用 XQuery
				 * 正则表达式，但我们在这里使用 Postgres 正则表达式。 'flags' 是在编译时
				 * 转换为整数标志的字符串字面量。
				 */
				JsonLikeRegexContext fc_lrcxt = {0};

				jspInitByBuffer(&fc_larg, fc_jsp->base,
								fc_jsp->content.like_regex.expr);

				return fc_executePredicate(fc_cxt, fc_jsp, &fc_larg, NULL, fc_jb, false,
										fc_executeLikeRegex, &fc_lrcxt);
			}

		case jpiExists:
			jspGetArg(fc_jsp, &fc_larg);

			if (jspStrictAbsenseOfErrors(fc_cxt))
			{
				/* 
				 * 在严格模式下，我们必须获得完整的值列表以
				 * 检查是否没有错误。
				 */
				JsonValueList fc_vals = {0};
				JsonPathExecResult fc_res =
				fc_executeItemOptUnwrapResultNoThrow(fc_cxt, &fc_larg, fc_jb,
												  false, &fc_vals);

				if (jperIsError(fc_res))
					return jpbUnknown;

				return fc_JsonValueListIsEmpty(&fc_vals) ? jpbFalse : jpbTrue;
			}
			else
			{
				JsonPathExecResult fc_res =
				fc_executeItemOptUnwrapResultNoThrow(fc_cxt, &fc_larg, fc_jb,
												  false, NULL);

				if (jperIsError(fc_res))
					return jpbUnknown;

				return fc_res == jperOk ? jpbTrue : jpbFalse;
			}

		default:
			elog(ERROR, "invalid boolean jsonpath item type: %d", fc_jsp->type);
			return jpbUnknown;
	}
}

/* 
 * 执行嵌套（过滤器等）布尔表达式，并将当前 SQL/JSON
 * 项推入栈中。
 */
static JsonPathBool fc_executeNestedBoolItem(JsonPathExecContext *fc_cxt, JsonPathItem *fc_jsp,
					  JsonbValue *fc_jb)
{
	JsonbValue *fc_prev;
	JsonPathBool fc_res;

	fc_prev = fc_cxt->current;
	fc_cxt->current = fc_jb;
	fc_res = fc_executeBoolItem(fc_cxt, fc_jsp, fc_jb, false);
	fc_cxt->current = fc_prev;

	return fc_res;
}

/* 
 * 实现多个 jsonpath 节点：
 *  - jpiAny (.** 访问器)，
 *  - jpiAnyKey (.* 访问器)，
 *  - jpiAnyArray ([*] 访问器)
 */
static JsonPathExecResult fc_executeAnyItem(JsonPathExecContext *fc_cxt, JsonPathItem *fc_jsp, JsonbContainer *fc_jbc,
			   JsonValueList *fc_found, uint32 fc_level, uint32 fc_first, uint32 fc_last,
			   bool fc_ignoreStructuralErrors, bool fc_unwrapNext)
{
	JsonPathExecResult fc_res = jperNotFound;
	JsonbIterator *fc_it;
	int32		fc_r;
	JsonbValue	fc_v;

	check_stack_depth();

	if (fc_level > fc_last)
		return fc_res;

	fc_it = JsonbIteratorInit(fc_jbc);

	/* 
	 * 递归遍历 jsonb 对象/数组
	 */
	while ((fc_r = JsonbIteratorNext(&fc_it, &fc_v, true)) != WJB_DONE)
	{
		if (fc_r == WJB_KEY)
		{
			fc_r = JsonbIteratorNext(&fc_it, &fc_v, true);
			Assert(fc_r == WJB_VALUE);
		}

		if (fc_r == WJB_VALUE || fc_r == WJB_ELEM)
		{

			if (fc_level >= fc_first ||
				(fc_first == PG_UINT32_MAX && fc_last == PG_UINT32_MAX &&
				 fc_v.type != jbvBinary))	/* 仅留下请求的内容 */
			{
				/* 检查表达式 */
				if (fc_jsp)
				{
					if (fc_ignoreStructuralErrors)
					{
						bool		fc_savedIgnoreStructuralErrors;

						fc_savedIgnoreStructuralErrors = fc_cxt->ignoreStructuralErrors;
						fc_cxt->ignoreStructuralErrors = true;
						fc_res = fc_executeItemOptUnwrapTarget(fc_cxt, fc_jsp, &fc_v, fc_found, fc_unwrapNext);
						fc_cxt->ignoreStructuralErrors = fc_savedIgnoreStructuralErrors;
					}
					else
						fc_res = fc_executeItemOptUnwrapTarget(fc_cxt, fc_jsp, &fc_v, fc_found, fc_unwrapNext);

					if (jperIsError(fc_res))
						break;

					if (fc_res == jperOk && !fc_found)
						break;
				}
				else if (fc_found)
					fc_JsonValueListAppend(fc_found, fc_copyJsonbValue(&fc_v));
				else
					return jperOk;
			}

			if (fc_level < fc_last && fc_v.type == jbvBinary)
			{
				fc_res = fc_executeAnyItem
					(fc_cxt, fc_jsp, fc_v.val.binary.data, fc_found,
					 fc_level + 1, fc_first, fc_last,
					 fc_ignoreStructuralErrors, fc_unwrapNext);

				if (jperIsError(fc_res))
					break;

				if (fc_res == jperOk && fc_found == NULL)
					break;
			}
		}
	}

	return fc_res;
}

/* 
 * 执行一元或二元谓词。
 *
 * 谓词具有存在语义，因为它们的操作数是项目
 * 序列。左侧和右侧操作数序列中的项目对
 * 被检查。仅当找到任何满足条件的配对时，返回 TRUE。
 * 在严格模式下，即使所需的配对已被找到，仍然需要检查所有配对以确认没有错误。
 * 如果发生任何错误，则返回 UNKNOWN（类似于 SQL NULL）。
 */
static JsonPathBool fc_executePredicate(JsonPathExecContext *fc_cxt, JsonPathItem *fc_pred,
				 JsonPathItem *fc_larg, JsonPathItem *fc_rarg, JsonbValue *fc_jb,
				 bool fc_unwrapRightArg, JsonPathPredicateCallback fc_exec,
				 void *fc_param)
{
	JsonPathExecResult fc_res;
	JsonValueListIterator fc_lseqit;
	JsonValueList fc_lseq = {0};
	JsonValueList fc_rseq = {0};
	JsonbValue *fc_lval;
	bool		fc_error = false;
	bool		fc_found = false;

	/* 左侧参数始终会自动解包。 */
	fc_res = fc_executeItemOptUnwrapResultNoThrow(fc_cxt, fc_larg, fc_jb, true, &fc_lseq);
	if (jperIsError(fc_res))
		return jpbUnknown;

	if (fc_rarg)
	{
		/* 右侧参数在条件下自动解包。 */
		fc_res = fc_executeItemOptUnwrapResultNoThrow(fc_cxt, fc_rarg, fc_jb,
												fc_unwrapRightArg, &fc_rseq);
		if (jperIsError(fc_res))
			return jpbUnknown;
	}

	fc_JsonValueListInitIterator(&fc_lseq, &fc_lseqit);
	while ((fc_lval = fc_JsonValueListNext(&fc_lseq, &fc_lseqit)))
	{
		JsonValueListIterator fc_rseqit;
		JsonbValue *fc_rval;
		bool		fc_first = true;

		fc_JsonValueListInitIterator(&fc_rseq, &fc_rseqit);
		if (fc_rarg)
			fc_rval = fc_JsonValueListNext(&fc_rseq, &fc_rseqit);
		else
			fc_rval = NULL;

		/* 遍历右侧参数序列，否则进行单次遍历 */
		while (fc_rarg ? (fc_rval != NULL) : fc_first)
		{
			JsonPathBool fc_res = fc_exec(fc_pred, fc_lval, fc_rval, fc_param);

			if (fc_res == jpbUnknown)
			{
				if (jspStrictAbsenseOfErrors(fc_cxt))
					return jpbUnknown;

				fc_error = true;
			}
			else if (fc_res == jpbTrue)
			{
				if (!jspStrictAbsenseOfErrors(fc_cxt))
					return jpbTrue;

				fc_found = true;
			}

			fc_first = false;
			if (fc_rarg)
				fc_rval = fc_JsonValueListNext(&fc_rseq, &fc_rseqit);
		}
	}

	if (fc_found)					/* 仅在严格模式下可能 */
		return jpbTrue;

	if (fc_error)					/* 仅在宽松模式下可能 */
		return jpbUnknown;

	return jpbFalse;
}

/* 
 * 对单个数值操作数执行二元算术表达式。
 * 数组操作数在宽松模式下会自动解包。
 */
static JsonPathExecResult fc_executeBinaryArithmExpr(JsonPathExecContext *fc_cxt, JsonPathItem *fc_jsp,
						JsonbValue *fc_jb, BinaryArithmFunc fc_func,
						JsonValueList *fc_found)
{
	JsonPathExecResult fc_jper;
	JsonPathItem fc_elem;
	JsonValueList fc_lseq = {0};
	JsonValueList fc_rseq = {0};
	JsonbValue *fc_lval;
	JsonbValue *fc_rval;
	Numeric		fc_res;

	jspGetLeftArg(fc_jsp, &fc_elem);

	/* 
	 * XXX：根据标准，仅乘法表达式的操作数是
	 * 解包的。我们将其扩展到其他二元算术表达式。
	 */
	fc_jper = fc_executeItemOptUnwrapResult(fc_cxt, &fc_elem, fc_jb, true, &fc_lseq);
	if (jperIsError(fc_jper))
		return fc_jper;

	jspGetRightArg(fc_jsp, &fc_elem);

	fc_jper = fc_executeItemOptUnwrapResult(fc_cxt, &fc_elem, fc_jb, true, &fc_rseq);
	if (jperIsError(fc_jper))
		return fc_jper;

	if (fc_JsonValueListLength(&fc_lseq) != 1 ||
		!(fc_lval = fc_getScalar(fc_JsonValueListHead(&fc_lseq), jbvNumeric)))
		RETURN_ERROR(ereport(ERROR,
							 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
							  errmsg("left operand of jsonpath operator %s is not a single numeric value",
									 jspOperationName(fc_jsp->type)))));

	if (fc_JsonValueListLength(&fc_rseq) != 1 ||
		!(fc_rval = fc_getScalar(fc_JsonValueListHead(&fc_rseq), jbvNumeric)))
		RETURN_ERROR(ereport(ERROR,
							 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
							  errmsg("right operand of jsonpath operator %s is not a single numeric value",
									 jspOperationName(fc_jsp->type)))));

	if (jspThrowErrors(fc_cxt))
	{
		fc_res = fc_func(fc_lval->val.numeric, fc_rval->val.numeric, NULL);
	}
	else
	{
		bool		fc_error = false;

		fc_res = fc_func(fc_lval->val.numeric, fc_rval->val.numeric, &fc_error);

		if (fc_error)
			return jperError;
	}

	if (!jspGetNext(fc_jsp, &fc_elem) && !fc_found)
		return jperOk;

	fc_lval = palloc(sizeof(*fc_lval));
	fc_lval->type = jbvNumeric;
	fc_lval->val.numeric = fc_res;

	return fc_executeNextItem(fc_cxt, fc_jsp, &fc_elem, fc_lval, fc_found, false);
}

/*
 * 对其操作数序列中的每个数值项执行一元算术表达式。
 * 数组操作数将在宽松模式下自动解除包装。
 */
static JsonPathExecResult fc_executeUnaryArithmExpr(JsonPathExecContext *fc_cxt, JsonPathItem *fc_jsp,
					   JsonbValue *fc_jb, PGFunction fc_func, JsonValueList *fc_found)
{
	JsonPathExecResult fc_jper;
	JsonPathExecResult fc_jper2;
	JsonPathItem fc_elem;
	JsonValueList fc_seq = {0};
	JsonValueListIterator fc_it;
	JsonbValue *fc_val;
	bool		fc_hasNext;

	jspGetArg(fc_jsp, &fc_elem);
	fc_jper = fc_executeItemOptUnwrapResult(fc_cxt, &fc_elem, fc_jb, true, &fc_seq);

	if (jperIsError(fc_jper))
		return fc_jper;

	fc_jper = jperNotFound;

	fc_hasNext = jspGetNext(fc_jsp, &fc_elem);

	fc_JsonValueListInitIterator(&fc_seq, &fc_it);
	while ((fc_val = fc_JsonValueListNext(&fc_seq, &fc_it)))
	{
		if ((fc_val = fc_getScalar(fc_val, jbvNumeric)))
		{
			if (!fc_found && !fc_hasNext)
				return jperOk;
		}
		else
		{
			if (!fc_found && !fc_hasNext)
				continue;		/* 跳过非数字处理 */

			RETURN_ERROR(ereport(ERROR,
								 (errcode(ERRCODE_SQL_JSON_NUMBER_NOT_FOUND),
								  errmsg("operand of unary jsonpath operator %s is not a numeric value",
										 jspOperationName(fc_jsp->type)))));
		}

		if (fc_func)
			fc_val->val.numeric =
				DatumGetNumeric(DirectFunctionCall1(fc_func,
													NumericGetDatum(fc_val->val.numeric)));

		fc_jper2 = fc_executeNextItem(fc_cxt, fc_jsp, &fc_elem, fc_val, fc_found, false);

		if (jperIsError(fc_jper2))
			return fc_jper2;

		if (fc_jper2 == jperOk)
		{
			if (!fc_found)
				return jperOk;
			fc_jper = jperOk;
		}
	}

	return fc_jper;
}

/*
 * STARTS_WITH 谓词回调。
 *
 * 检查“whole”字符串是否以“initial”字符串开头。
 */
static JsonPathBool fc_executeStartsWith(JsonPathItem *fc_jsp, JsonbValue *fc_whole, JsonbValue *fc_initial,
				  void *fc_param)
{
	if (!(fc_whole = fc_getScalar(fc_whole, jbvString)))
		return jpbUnknown;		/* 错误 */

	if (!(fc_initial = fc_getScalar(fc_initial, jbvString)))
		return jpbUnknown;		/* 错误 */

	if (fc_whole->val.string.len >= fc_initial->val.string.len &&
		!memcmp(fc_whole->val.string.val,
				fc_initial->val.string.val,
				fc_initial->val.string.len))
		return jpbTrue;

	return jpbFalse;
}

/*
 * LIKE_REGEX 谓词回调。
 *
 * 检查字符串是否与正则表达式模式匹配。
 */
static JsonPathBool fc_executeLikeRegex(JsonPathItem *fc_jsp, JsonbValue *fc_str, JsonbValue *fc_rarg,
				 void *fc_param)
{
	JsonLikeRegexContext *fc_cxt = fc_param;

	if (!(fc_str = fc_getScalar(fc_str, jbvString)))
		return jpbUnknown;

	/* 缓存正则表达式文本和转换标志。 */
	if (!fc_cxt->regex)
	{
		fc_cxt->regex =
			cstring_to_text_with_len(fc_jsp->content.like_regex.pattern,
									 fc_jsp->content.like_regex.patternlen);
		fc_cxt->cflags = jspConvertRegexFlags(fc_jsp->content.like_regex.flags);
	}

	if (RE_compile_and_execute(fc_cxt->regex, fc_str->val.string.val,
							   fc_str->val.string.len,
							   fc_cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
		return jpbTrue;

	return jpbFalse;
}

/*
 * 使用指定的用户函数“func”执行数字项方法（.abs(), .floor(), .ceil()）。
 */
static JsonPathExecResult fc_executeNumericItemMethod(JsonPathExecContext *fc_cxt, JsonPathItem *fc_jsp,
						 JsonbValue *fc_jb, bool fc_unwrap, PGFunction fc_func,
						 JsonValueList *fc_found)
{
	JsonPathItem fc_next;
	Datum		fc_datum;

	if (fc_unwrap && fc_JsonbType(fc_jb) == jbvArray)
		return fc_executeItemUnwrapTargetArray(fc_cxt, fc_jsp, fc_jb, fc_found, false);

	if (!(fc_jb = fc_getScalar(fc_jb, jbvNumeric)))
		RETURN_ERROR(ereport(ERROR,
							 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
							  errmsg("jsonpath item method .%s() can only be applied to a numeric value",
									 jspOperationName(fc_jsp->type)))));

	fc_datum = DirectFunctionCall1(fc_func, NumericGetDatum(fc_jb->val.numeric));

	if (!jspGetNext(fc_jsp, &fc_next) && !fc_found)
		return jperOk;

	fc_jb = palloc(sizeof(*fc_jb));
	fc_jb->type = jbvNumeric;
	fc_jb->val.numeric = DatumGetNumeric(fc_datum);

	return fc_executeNextItem(fc_cxt, fc_jsp, &fc_next, fc_jb, fc_found, false);
}

/*
 * .datetime() 方法的实现。
 *
 * 将字符串转换为日期/时间值。实际类型在运行时确定。
 * 如果提供了参数，则此参数用作模板字符串。
 * 否则，选择第一个合适的 ISO 格式。
 */
static JsonPathExecResult fc_executeDateTimeMethod(JsonPathExecContext *fc_cxt, JsonPathItem *fc_jsp,
					  JsonbValue *fc_jb, JsonValueList *fc_found)
{
	JsonbValue	fc_jbvbuf;
	Datum		fc_value;
	text	   *fc_datetime;
	Oid			fc_collid;
	Oid			fc_typid;
	int32		fc_typmod = -1;
	int			fc_tz = 0;
	bool		fc_hasNext;
	JsonPathExecResult fc_res = jperNotFound;
	JsonPathItem fc_elem;

	if (!(fc_jb = fc_getScalar(fc_jb, jbvString)))
		RETURN_ERROR(ereport(ERROR,
							 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
							  errmsg("jsonpath item method .%s() can only be applied to a string",
									 jspOperationName(fc_jsp->type)))));

	fc_datetime = cstring_to_text_with_len(fc_jb->val.string.val,
										fc_jb->val.string.len);

	/*
	 * 在某些情况下，我们可能希望调用者提供要使用的排序规则，
	 * 但现在还不清楚他们是否能够做得比 DEFAULT_COLLATION_OID 更好。
	 */
	fc_collid = DEFAULT_COLLATION_OID;

	if (fc_jsp->content.arg)
	{
		text	   *fc_template;
		char	   *fc_template_str;
		int			fc_template_len;
		bool		fc_have_error = false;

		jspGetArg(fc_jsp, &fc_elem);

		if (fc_elem.type != jpiString)
			elog(ERROR, "invalid jsonpath item type for .datetime() argument");

		fc_template_str = jspGetString(&fc_elem, &fc_template_len);

		fc_template = cstring_to_text_with_len(fc_template_str,
											fc_template_len);

		fc_value = parse_datetime(fc_datetime, fc_template, fc_collid, true,
							   &fc_typid, &fc_typmod, &fc_tz,
							   jspThrowErrors(fc_cxt) ? NULL : &fc_have_error);

		if (fc_have_error)
			fc_res = jperError;
		else
			fc_res = jperOk;
	}
	else
	{
		/*
		 * 根据 SQL/JSON 标准列举 ISO 格式：日期、
		 * timetz、时间、timestamptz、时间戳。
		 *
		 * 我们还支持时间戳的 ISO 8601 格式（带“T”），因为
		 * to_json[b]() 函数使用此格式。
		 */
		static const char *fc_fmt_str[] =
		{
			"yyyy-mm-dd",		/* date */
			"HH24:MI:SS.USTZH:TZM", /* timetz */
			"HH24:MI:SS.USTZH",
			"HH24:MI:SSTZH:TZM",
			"HH24:MI:SSTZH",
			"HH24:MI:SS.US",	/* 无时区的时间 */
			"HH24:MI:SS",
			"yyyy-mm-dd HH24:MI:SS.USTZH:TZM",	/* 带时区的时间戳 */
			"yyyy-mm-dd HH24:MI:SS.USTZH",
			"yyyy-mm-dd HH24:MI:SSTZH:TZM",
			"yyyy-mm-dd HH24:MI:SSTZH",
			"yyyy-mm-dd\"T\"HH24:MI:SS.USTZH:TZM",
			"yyyy-mm-dd\"T\"HH24:MI:SS.USTZH",
			"yyyy-mm-dd\"T\"HH24:MI:SSTZH:TZM",
			"yyyy-mm-dd\"T\"HH24:MI:SSTZH",
			"yyyy-mm-dd HH24:MI:SS.US", /* 无时区的时间戳 */
			"yyyy-mm-dd HH24:MI:SS",
			"yyyy-mm-dd\"T\"HH24:MI:SS.US",
			"yyyy-mm-dd\"T\"HH24:MI:SS"
		};

		/* 格式文本的缓存 */
		static text *fc_fmt_txt[lengthof(fc_fmt_str)] = {0};
		int			fc_i;

		/* 循环直到日期时间格式匹配 */
		for (fc_i = 0; fc_i < lengthof(fc_fmt_str); fc_i++)
		{
			bool		fc_have_error = false;

			if (!fc_fmt_txt[fc_i])
			{
				MemoryContext fc_oldcxt =
				MemoryContextSwitchTo(TopMemoryContext);

				fc_fmt_txt[fc_i] = cstring_to_text(fc_fmt_str[fc_i]);
				MemoryContextSwitchTo(fc_oldcxt);
			}

			fc_value = parse_datetime(fc_datetime, fc_fmt_txt[fc_i], fc_collid, true,
								   &fc_typid, &fc_typmod, &fc_tz,
								   &fc_have_error);

			if (!fc_have_error)
			{
				fc_res = jperOk;
				break;
			}
		}

		if (fc_res == jperNotFound)
			RETURN_ERROR(ereport(ERROR,
								 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
								  errmsg("datetime format is not recognized: \"%s\"",
										 text_to_cstring(fc_datetime)),
								  errhint("Use a datetime template argument to specify the input data format."))));
	}

	pfree(fc_datetime);

	if (jperIsError(fc_res))
		return fc_res;

	fc_hasNext = jspGetNext(fc_jsp, &fc_elem);

	if (!fc_hasNext && !fc_found)
		return fc_res;

	fc_jb = fc_hasNext ? &fc_jbvbuf : palloc(sizeof(*fc_jb));

	fc_jb->type = jbvDatetime;
	fc_jb->val.datetime.value = fc_value;
	fc_jb->val.datetime.typid = fc_typid;
	fc_jb->val.datetime.typmod = fc_typmod;
	fc_jb->val.datetime.tz = fc_tz;

	return fc_executeNextItem(fc_cxt, fc_jsp, &fc_elem, fc_jb, fc_found, fc_hasNext);
}

/*
 * .keyvalue() 方法的实现。
 *
 * .keyvalue() 方法返回对象的键值对序列，格式为：'{ "key": key, "value": value, "id": id }'。
 *
 * “id”字段是一个对象标识符，由两个部分构成：
 * 基本对象 id 和其在基本对象 jsonb 中的二进制偏移量：
 * id = 10000000000 * base_object_id + obj_offset_in_base_object
 *
 * 10000000000（10^10）--是大于 2^32 的首个十进制数
 * （jsonb 中的最大偏移量）。这里使用十进制乘数是为了提高
 * 标识符的可读性。
 *
 * 基本对象通常是路径的根对象：上下文项“$”或路径
 * 变量“$var”，字面量暂时无法生成对象。但如果路径
 * 包含生成的对象（例如 .keyvalue() 本身），那么它们
 * 将成为后续 .keyvalue() 的基本对象。
 *
 * “$”的 id 是 0。“$var”的 id 是变量列表中
 * 的序号（正数）（见 getJsonPathVariable()）。为生成的对象
 * 分配的 id 使用全局计数器 JsonPathExecContext.lastGeneratedObjectId。
 */
static JsonPathExecResult fc_executeKeyValueMethod(JsonPathExecContext *fc_cxt, JsonPathItem *fc_jsp,
					  JsonbValue *fc_jb, JsonValueList *fc_found)
{
	JsonPathExecResult fc_res = jperNotFound;
	JsonPathItem fc_next;
	JsonbContainer *fc_jbc;
	JsonbValue	fc_key;
	JsonbValue	fc_val;
	JsonbValue	fc_idval;
	JsonbValue	fc_keystr;
	JsonbValue	fc_valstr;
	JsonbValue	fc_idstr;
	JsonbIterator *fc_it;
	JsonbIteratorToken fc_tok;
	int64		fc_id;
	bool		fc_hasNext;

	if (fc_JsonbType(fc_jb) != jbvObject || fc_jb->type != jbvBinary)
		RETURN_ERROR(ereport(ERROR,
							 (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
							  errmsg("jsonpath item method .%s() can only be applied to an object",
									 jspOperationName(fc_jsp->type)))));

	fc_jbc = fc_jb->val.binary.data;

	if (!JsonContainerSize(fc_jbc))
		return jperNotFound;	/* 无键值对 */

	fc_hasNext = jspGetNext(fc_jsp, &fc_next);

	fc_keystr.type = jbvString;
	fc_keystr.val.string.val = "key";
	fc_keystr.val.string.len = 3;

	fc_valstr.type = jbvString;
	fc_valstr.val.string.val = "value";
	fc_valstr.val.string.len = 5;

	fc_idstr.type = jbvString;
	fc_idstr.val.string.val = "id";
	fc_idstr.val.string.len = 2;

	/* 从其基本对象和内部偏移量构建对象 id */
	fc_id = fc_jb->type != jbvBinary ? 0 :
		(int64) ((char *) fc_jbc - (char *) fc_cxt->baseObject.jbc);
	fc_id += (int64) fc_cxt->baseObject.id * INT64CONST(10000000000);

	fc_idval.type = jbvNumeric;
	fc_idval.val.numeric = int64_to_numeric(fc_id);

	fc_it = JsonbIteratorInit(fc_jbc);

	while ((fc_tok = JsonbIteratorNext(&fc_it, &fc_key, true)) != WJB_DONE)
	{
		JsonBaseObjectInfo fc_baseObject;
		JsonbValue	fc_obj;
		JsonbParseState *fc_ps;
		JsonbValue *fc_keyval;
		Jsonb	   *fc_jsonb;

		if (fc_tok != WJB_KEY)
			continue;

		fc_res = jperOk;

		if (!fc_hasNext && !fc_found)
			break;

		fc_tok = JsonbIteratorNext(&fc_it, &fc_val, true);
		Assert(fc_tok == WJB_VALUE);

		fc_ps = NULL;
		pushJsonbValue(&fc_ps, WJB_BEGIN_OBJECT, NULL);

		pushJsonbValue(&fc_ps, WJB_KEY, &fc_keystr);
		pushJsonbValue(&fc_ps, WJB_VALUE, &fc_key);

		pushJsonbValue(&fc_ps, WJB_KEY, &fc_valstr);
		pushJsonbValue(&fc_ps, WJB_VALUE, &fc_val);

		pushJsonbValue(&fc_ps, WJB_KEY, &fc_idstr);
		pushJsonbValue(&fc_ps, WJB_VALUE, &fc_idval);

		fc_keyval = pushJsonbValue(&fc_ps, WJB_END_OBJECT, NULL);

		fc_jsonb = JsonbValueToJsonb(fc_keyval);

		fc_JsonbInitBinary(&fc_obj, fc_jsonb);

		fc_baseObject = fc_setBaseObject(fc_cxt, &fc_obj, fc_cxt->lastGeneratedObjectId++);

		fc_res = fc_executeNextItem(fc_cxt, fc_jsp, &fc_next, &fc_obj, fc_found, true);

		fc_cxt->baseObject = fc_baseObject;

		if (jperIsError(fc_res))
			return fc_res;

		if (fc_res == jperOk && !fc_found)
			break;
	}

	return fc_res;
}

/*
 * 将布尔执行状态“res”转换为布尔 JSON 项并执行
 * 下一个 jsonpath。
 */
static JsonPathExecResult fc_appendBoolResult(JsonPathExecContext *fc_cxt, JsonPathItem *fc_jsp,
				 JsonValueList *fc_found, JsonPathBool fc_res)
{
	JsonPathItem fc_next;
	JsonbValue	fc_jbv;

	if (!jspGetNext(fc_jsp, &fc_next) && !fc_found)
		return jperOk;			/* 找到单例布尔值 */

	if (fc_res == jpbUnknown)
	{
		fc_jbv.type = jbvNull;
	}
	else
	{
		fc_jbv.type = jbvBool;
		fc_jbv.val.boolean = fc_res == jpbTrue;
	}

	return fc_executeNextItem(fc_cxt, fc_jsp, &fc_next, &fc_jbv, fc_found, true);
}

/*
 * 将 jsonpath 的标量或变量节点转换为实际 jsonb 值。
 *
 * 如果节点是变量，则返回其 id，否则返回 0。
 */
static void fc_getJsonPathItem(JsonPathExecContext *fc_cxt, JsonPathItem *fc_item,
				JsonbValue *fc_value)
{
	switch (fc_item->type)
	{
		case jpiNull:
			fc_value->type = jbvNull;
			break;
		case jpiBool:
			fc_value->type = jbvBool;
			fc_value->val.boolean = jspGetBool(fc_item);
			break;
		case jpiNumeric:
			fc_value->type = jbvNumeric;
			fc_value->val.numeric = jspGetNumeric(fc_item);
			break;
		case jpiString:
			fc_value->type = jbvString;
			fc_value->val.string.val = jspGetString(fc_item,
												 &fc_value->val.string.len);
			break;
		case jpiVariable:
			fc_getJsonPathVariable(fc_cxt, fc_item, fc_cxt->vars, fc_value);
			return;
		default:
			elog(ERROR, "unexpected jsonpath item type");
	}
}

/*
 * 获取传递给 jsonpath 执行器的变量值
 */
static void fc_getJsonPathVariable(JsonPathExecContext *fc_cxt, JsonPathItem *fc_variable,
					Jsonb *fc_vars, JsonbValue *fc_value)
{
	char	   *fc_varName;
	int			fc_varNameLength;
	JsonbValue	fc_tmp;
	JsonbValue *fc_v;

	if (!fc_vars)
	{
		fc_value->type = jbvNull;
		return;
	}

	Assert(fc_variable->type == jpiVariable);
	fc_varName = jspGetString(fc_variable, &fc_varNameLength);
	fc_tmp.type = jbvString;
	fc_tmp.val.string.val = fc_varName;
	fc_tmp.val.string.len = fc_varNameLength;

	fc_v = findJsonbValueFromContainer(&fc_vars->root, JB_FOBJECT, &fc_tmp);

	if (fc_v)
	{
		*fc_value = *fc_v;
		pfree(fc_v);
	}
	else
	{
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("could not find jsonpath variable \"%s\"",
						pnstrdup(fc_varName, fc_varNameLength))));
	}

	fc_JsonbInitBinary(&fc_tmp, fc_vars);
	fc_setBaseObject(fc_cxt, &fc_tmp, 1);
}

/**************** JsonPath 执行的支持函数 *****************/

/*
 * 返回数组项的大小，如果项不是数组则返回 -1。
 */
static int fc_JsonbArraySize(JsonbValue *fc_jb)
{
	Assert(fc_jb->type != jbvArray);

	if (fc_jb->type == jbvBinary)
	{
		JsonbContainer *fc_jbc = fc_jb->val.binary.data;

		if (JsonContainerIsArray(fc_jbc) && !JsonContainerIsScalar(fc_jbc))
			return JsonContainerSize(fc_jbc);
	}

	return -1;
}

/* 比较谓词回调。 */
static JsonPathBool fc_executeComparison(JsonPathItem *fc_cmp, JsonbValue *fc_lv, JsonbValue *fc_rv, void *fc_p)
{
	JsonPathExecContext *fc_cxt = (JsonPathExecContext *) fc_p;

	return fc_compareItems(fc_cmp->type, fc_lv, fc_rv, fc_cxt->useTz);
}

/*
 * 执行两个字符串的逐字节比较。
 */
static int fc_binaryCompareStrings(const char *fc_s1, int fc_len1,
					 const char *fc_s2, int fc_len2)
{
	int			fc_cmp;

	fc_cmp = memcmp(fc_s1, fc_s2, Min(fc_len1, fc_len2));

	if (fc_cmp != 0)
		return fc_cmp;

	if (fc_len1 == fc_len2)
		return 0;

	return fc_len1 < fc_len2 ? -1 : 1;
}

/*
 * 使用 Unicode 码点排序比较两个字符串。
 */
static int fc_compareStrings(const char *fc_mbstr1, int fc_mblen1,
			   const char *fc_mbstr2, int fc_mblen2)
{
	if (GetDatabaseEncoding() == PG_SQL_ASCII ||
		GetDatabaseEncoding() == PG_UTF8)
	{
		/*
		 * 众所周知，UTF-8 字符串的逐字节比较
		 * 结果与码点比较结果相匹配。ASCII 可以被
		 * 视为 UTF-8 的特例。
		 */
		return fc_binaryCompareStrings(fc_mbstr1, fc_mblen1, fc_mbstr2, fc_mblen2);
	}
	else
	{
		char	   *fc_utf8str1,
				   *fc_utf8str2;
		int			fc_cmp,
					fc_utf8len1,
					fc_utf8len2;

		/*
		 * 我们必须先将其他编码转换为 UTF-8，然后进行比较。
		 * 输入字符串可能不是以 null 结束的，pg_server_to_any() 可能
		 * 会“原样”返回它们。因此，只有在实际
		 * 转换时才使用 strlen()。
		 */
		fc_utf8str1 = pg_server_to_any(fc_mbstr1, fc_mblen1, PG_UTF8);
		fc_utf8str2 = pg_server_to_any(fc_mbstr2, fc_mblen2, PG_UTF8);
		fc_utf8len1 = (fc_mbstr1 == fc_utf8str1) ? fc_mblen1 : strlen(fc_utf8str1);
		fc_utf8len2 = (fc_mbstr2 == fc_utf8str2) ? fc_mblen2 : strlen(fc_utf8str2);

		fc_cmp = fc_binaryCompareStrings(fc_utf8str1, fc_utf8len1, fc_utf8str2, fc_utf8len2);

		/*
		 * 如果 pg_server_to_any() 没有进行真实的转换，那么我们实际上
		 * 已经比较了原始字符串。因此，我们已经完成了。
		 */
		if (fc_mbstr1 == fc_utf8str1 && fc_mbstr2 == fc_utf8str2)
			return fc_cmp;

		/* 如有需要，释放内存 */
		if (fc_mbstr1 != fc_utf8str1)
			pfree(fc_utf8str1);
		if (fc_mbstr2 != fc_utf8str2)
			pfree(fc_utf8str2);

		/*
		 * 当所有 Unicode 码点相等时，返回二进制
		 * 比较的结果。在某些边缘情况中，相同的字符在编码中可能有不同的
		 * 表示方式。那样的话，我们的行为可能与
		 * 标准不一致。然而，这使我们能够对 "==" 操作符执行简单的二进制比较，
		 * 这在典型情况下对性能至关重要。
		 * 将来为了实现严格的标准符合性，我们可以对输入的 JSON 字符串进行
		 * 规范化处理。
		 */
		if (fc_cmp == 0)
			return fc_binaryCompareStrings(fc_mbstr1, fc_mblen1, fc_mbstr2, fc_mblen2);
		else
			return fc_cmp;
	}
}

/*
 * 比较两个 SQL/JSON 项，使用比较操作 'op'。
 */
static JsonPathBool fc_compareItems(int32 fc_op, JsonbValue *fc_jb1, JsonbValue *fc_jb2, bool fc_useTz)
{
	int			fc_cmp;
	bool		fc_res;

	if (fc_jb1->type != fc_jb2->type)
	{
		if (fc_jb1->type == jbvNull || fc_jb2->type == jbvNull)

			/*
			 * 对 null 和非 null 的相等性和顺序比较始终返回
			 * false，但不相等比较返回 true。
			 */
			return fc_op == jpiNotEqual ? jpbTrue : jpbFalse;

		/* 不同类型的非 null 项不可比较。 */
		return jpbUnknown;
	}

	switch (fc_jb1->type)
	{
		case jbvNull:
			fc_cmp = 0;
			break;
		case jbvBool:
			fc_cmp = fc_jb1->val.boolean == fc_jb2->val.boolean ? 0 :
				fc_jb1->val.boolean ? 1 : -1;
			break;
		case jbvNumeric:
			fc_cmp = fc_compareNumeric(fc_jb1->val.numeric, fc_jb2->val.numeric);
			break;
		case jbvString:
			if (fc_op == jpiEqual)
				return fc_jb1->val.string.len != fc_jb2->val.string.len ||
					memcmp(fc_jb1->val.string.val,
						   fc_jb2->val.string.val,
						   fc_jb1->val.string.len) ? jpbFalse : jpbTrue;

			fc_cmp = fc_compareStrings(fc_jb1->val.string.val, fc_jb1->val.string.len,
								 fc_jb2->val.string.val, fc_jb2->val.string.len);
			break;
		case jbvDatetime:
			{
				bool		fc_cast_error;

				fc_cmp = fc_compareDatetime(fc_jb1->val.datetime.value,
									  fc_jb1->val.datetime.typid,
									  fc_jb2->val.datetime.value,
									  fc_jb2->val.datetime.typid,
									  fc_useTz,
									  &fc_cast_error);

				if (fc_cast_error)
					return jpbUnknown;
			}
			break;

		case jbvBinary:
		case jbvArray:
		case jbvObject:
			return jpbUnknown;	/* 非标量不可比较 */

		default:
			elog(ERROR, "invalid jsonb value type %d", fc_jb1->type);
	}

	switch (fc_op)
	{
		case jpiEqual:
			fc_res = (fc_cmp == 0);
			break;
		case jpiNotEqual:
			fc_res = (fc_cmp != 0);
			break;
		case jpiLess:
			fc_res = (fc_cmp < 0);
			break;
		case jpiGreater:
			fc_res = (fc_cmp > 0);
			break;
		case jpiLessOrEqual:
			fc_res = (fc_cmp <= 0);
			break;
		case jpiGreaterOrEqual:
			fc_res = (fc_cmp >= 0);
			break;
		default:
			elog(ERROR, "unrecognized jsonpath operation: %d", fc_op);
			return jpbUnknown;
	}

	return fc_res ? jpbTrue : jpbFalse;
}

/* 比较两个数字 */
static int fc_compareNumeric(Numeric fc_a, Numeric fc_b)
{
	return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
											 NumericGetDatum(fc_a),
											 NumericGetDatum(fc_b)));
}

static JsonbValue * fc_copyJsonbValue(JsonbValue *fc_src)
{
	JsonbValue *fc_dst = palloc(sizeof(*fc_dst));

	*fc_dst = *fc_src;

	return fc_dst;
}

/*
 * 执行数组下标表达式，并将结果数字项转换为
 * 整数类型，并截断。
 */
static JsonPathExecResult fc_getArrayIndex(JsonPathExecContext *fc_cxt, JsonPathItem *fc_jsp, JsonbValue *fc_jb,
			  int32 *fc_index)
{
	JsonbValue *fc_jbv;
	JsonValueList fc_found = {0};
	JsonPathExecResult fc_res = fc_executeItem(fc_cxt, fc_jsp, fc_jb, &fc_found);
	Datum		fc_numeric_index;
	bool		fc_have_error = false;

	if (jperIsError(fc_res))
		return fc_res;

	if (fc_JsonValueListLength(&fc_found) != 1 ||
		!(fc_jbv = fc_getScalar(fc_JsonValueListHead(&fc_found), jbvNumeric)))
		RETURN_ERROR(ereport(ERROR,
							 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
							  errmsg("jsonpath array subscript is not a single numeric value"))));

	fc_numeric_index = DirectFunctionCall2(numeric_trunc,
										NumericGetDatum(fc_jbv->val.numeric),
										Int32GetDatum(0));

	*fc_index = numeric_int4_opt_error(DatumGetNumeric(fc_numeric_index),
									&fc_have_error);

	if (fc_have_error)
		RETURN_ERROR(ereport(ERROR,
							 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
							  errmsg("jsonpath array subscript is out of integer range"))));

	return jperOk;
}

/* 保存需要执行 .keyvalue() 的基础对象及其 id。 */
static JsonBaseObjectInfo
fc_setBaseObject(JsonPathExecContext *fc_cxt, JsonbValue *fc_jbv, int32 fc_id)
{
	JsonBaseObjectInfo fc_baseObject = fc_cxt->baseObject;

	fc_cxt->baseObject.jbc = fc_jbv->type != jbvBinary ? NULL :
		(JsonbContainer *) fc_jbv->val.binary.data;
	fc_cxt->baseObject.id = fc_id;

	return fc_baseObject;
}

static void fc_JsonValueListAppend(JsonValueList *fc_jvl, JsonbValue *fc_jbv)
{
	if (fc_jvl->singleton)
	{
		fc_jvl->list = list_make2(fc_jvl->singleton, fc_jbv);
		fc_jvl->singleton = NULL;
	}
	else if (!fc_jvl->list)
		fc_jvl->singleton = fc_jbv;
	else
		fc_jvl->list = lappend(fc_jvl->list, fc_jbv);
}

static int fc_JsonValueListLength(const JsonValueList *fc_jvl)
{
	return fc_jvl->singleton ? 1 : list_length(fc_jvl->list);
}

static bool fc_JsonValueListIsEmpty(JsonValueList *fc_jvl)
{
	return !fc_jvl->singleton && list_length(fc_jvl->list) <= 0;
}

static JsonbValue * fc_JsonValueListHead(JsonValueList *fc_jvl)
{
	return fc_jvl->singleton ? fc_jvl->singleton : linitial(fc_jvl->list);
}

static List * fc_JsonValueListGetList(JsonValueList *fc_jvl)
{
	if (fc_jvl->singleton)
		return list_make1(fc_jvl->singleton);

	return fc_jvl->list;
}

static void fc_JsonValueListInitIterator(const JsonValueList *fc_jvl, JsonValueListIterator *fc_it)
{
	if (fc_jvl->singleton)
	{
		fc_it->value = fc_jvl->singleton;
		fc_it->list = NIL;
		fc_it->next = NULL;
	}
	else if (fc_jvl->list != NIL)
	{
		fc_it->value = (JsonbValue *) linitial(fc_jvl->list);
		fc_it->list = fc_jvl->list;
		fc_it->next = list_second_cell(fc_jvl->list);
	}
	else
	{
		fc_it->value = NULL;
		fc_it->list = NIL;
		fc_it->next = NULL;
	}
}

/*
 * 从序列推进迭代器获取下一个项。
 */
static JsonbValue * fc_JsonValueListNext(const JsonValueList *fc_jvl, JsonValueListIterator *fc_it)
{
	JsonbValue *fc_result = fc_it->value;

	if (fc_it->next)
	{
		fc_it->value = lfirst(fc_it->next);
		fc_it->next = lnext(fc_it->list, fc_it->next);
	}
	else
	{
		fc_it->value = NULL;
	}

	return fc_result;
}

/*
 * 使用给定的 jsonb 容器初始化一个二进制 JsonbValue。
 */
static JsonbValue * fc_JsonbInitBinary(JsonbValue *fc_jbv, Jsonb *fc_jb)
{
	fc_jbv->type = jbvBinary;
	fc_jbv->val.binary.data = &fc_jb->root;
	fc_jbv->val.binary.len = VARSIZE_ANY_EXHDR(fc_jb);

	return fc_jbv;
}

/*
 * 返回 JsonbValue 的 jbv* 类型。注意，它不会直接返回 jbvBinary。
 */
static int fc_JsonbType(JsonbValue *fc_jb)
{
	int			fc_type = fc_jb->type;

	if (fc_jb->type == jbvBinary)
	{
		JsonbContainer *fc_jbc = (void *) fc_jb->val.binary.data;

		/* 在 jsonpath 执行期间应始终提取标量。 */
		Assert(!JsonContainerIsScalar(fc_jbc));

		if (JsonContainerIsObject(fc_jbc))
			fc_type = jbvObject;
		else if (JsonContainerIsArray(fc_jbc))
			fc_type = jbvArray;
		else
			elog(ERROR, "invalid jsonb container type: 0x%08x", fc_jbc->header);
	}

	return fc_type;
}

/* 获取给定类型的标量，或在类型不匹配时返回 NULL */
static JsonbValue * fc_getScalar(JsonbValue *fc_scalar, enum jbvType fc_type)
{
	/* 在 jsonpath 执行期间应始终提取标量。 */
	Assert(fc_scalar->type != jbvBinary ||
		   !JsonContainerIsScalar(fc_scalar->val.binary.data));

	return fc_scalar->type == fc_type ? fc_scalar : NULL;
}

/* 从项列表构建一个 JSON 数组 */
static JsonbValue * fc_wrapItemsInArray(const JsonValueList *fc_items)
{
	JsonbParseState *fc_ps = NULL;
	JsonValueListIterator fc_it;
	JsonbValue *fc_jbv;

	pushJsonbValue(&fc_ps, WJB_BEGIN_ARRAY, NULL);

	fc_JsonValueListInitIterator(fc_items, &fc_it);
	while ((fc_jbv = fc_JsonValueListNext(fc_items, &fc_it)))
		pushJsonbValue(&fc_ps, WJB_ELEM, fc_jbv);

	return pushJsonbValue(&fc_ps, WJB_END_ARRAY, NULL);
}

/* 检查转换类型1到类型2所需的时区是否被使用 */
static void fc_checkTimezoneIsUsedForCast(bool fc_useTz, const char *fc_type1, const char *fc_type2)
{
	if (!fc_useTz)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("cannot convert value from %s to %s without time zone usage",
						fc_type1, fc_type2),
				 errhint("Use *_tz() function for time zone support.")));
}

/* 将时间数据转换为 timetz 数据 */
static Datum fc_castTimeToTimeTz(Datum fc_time, bool fc_useTz)
{
	fc_checkTimezoneIsUsedForCast(fc_useTz, "time", "timetz");

	return DirectFunctionCall1(time_timetz, fc_time);
}

/*
 * 比较日期和时间戳。
 * 注意，这不涉及任何时区考虑。
 */
static int fc_cmpDateToTimestamp(DateADT fc_date1, Timestamp fc_ts2, bool fc_useTz)
{
	return date_cmp_timestamp_internal(fc_date1, fc_ts2);
}

/*
 * 比较日期和 timestamptz。
 */
static int fc_cmpDateToTimestampTz(DateADT fc_date1, TimestampTz fc_tstz2, bool fc_useTz)
{
	fc_checkTimezoneIsUsedForCast(fc_useTz, "date", "timestamptz");

	return date_cmp_timestamptz_internal(fc_date1, fc_tstz2);
}

/*
 * 比较时间戳和 timestamptz。
 */
static int fc_cmpTimestampToTimestampTz(Timestamp fc_ts1, TimestampTz fc_tstz2, bool fc_useTz)
{
	fc_checkTimezoneIsUsedForCast(fc_useTz, "timestamp", "timestamptz");

	return timestamp_cmp_timestamptz_internal(fc_ts1, fc_tstz2);
}

/*
 * 两个不同类型的 datetime SQL/JSON 项的跨类型比较。如果项不可比较，
 * *cast_error 标志被设置，否则 *cast_error 被取消。
 * 如果转换需要时区而未使用，则抛出显式错误。
 */
static int fc_compareDatetime(Datum fc_val1, Oid fc_typid1, Datum fc_val2, Oid fc_typid2,
				bool fc_useTz, bool *fc_cast_error)
{
	PGFunction	fc_cmpfunc;

	*fc_cast_error = false;

	switch (fc_typid1)
	{
		case DATEOID:
			switch (fc_typid2)
			{
				case DATEOID:
					fc_cmpfunc = date_cmp;

					break;

				case TIMESTAMPOID:
					return fc_cmpDateToTimestamp(DatumGetDateADT(fc_val1),
											  DatumGetTimestamp(fc_val2),
											  fc_useTz);

				case TIMESTAMPTZOID:
					return fc_cmpDateToTimestampTz(DatumGetDateADT(fc_val1),
												DatumGetTimestampTz(fc_val2),
												fc_useTz);

				case TIMEOID:
				case TIMETZOID:
					*fc_cast_error = true; /* 不可比较的类型 */
					return 0;

				default:
					elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
						 fc_typid2);
			}
			break;

		case TIMEOID:
			switch (fc_typid2)
			{
				case TIMEOID:
					fc_cmpfunc = time_cmp;

					break;

				case TIMETZOID:
					fc_val1 = fc_castTimeToTimeTz(fc_val1, fc_useTz);
					fc_cmpfunc = timetz_cmp;

					break;

				case DATEOID:
				case TIMESTAMPOID:
				case TIMESTAMPTZOID:
					*fc_cast_error = true; /* 不可比较的类型 */
					return 0;

				default:
					elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
						 fc_typid2);
			}
			break;

		case TIMETZOID:
			switch (fc_typid2)
			{
				case TIMEOID:
					fc_val2 = fc_castTimeToTimeTz(fc_val2, fc_useTz);
					fc_cmpfunc = timetz_cmp;

					break;

				case TIMETZOID:
					fc_cmpfunc = timetz_cmp;

					break;

				case DATEOID:
				case TIMESTAMPOID:
				case TIMESTAMPTZOID:
					*fc_cast_error = true; /* 不可比较的类型 */
					return 0;

				default:
					elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
						 fc_typid2);
			}
			break;

		case TIMESTAMPOID:
			switch (fc_typid2)
			{
				case DATEOID:
					return -fc_cmpDateToTimestamp(DatumGetDateADT(fc_val2),
											   DatumGetTimestamp(fc_val1),
											   fc_useTz);

				case TIMESTAMPOID:
					fc_cmpfunc = timestamp_cmp;

					break;

				case TIMESTAMPTZOID:
					return fc_cmpTimestampToTimestampTz(DatumGetTimestamp(fc_val1),
													 DatumGetTimestampTz(fc_val2),
													 fc_useTz);

				case TIMEOID:
				case TIMETZOID:
					*fc_cast_error = true; /* 不可比较的类型 */
					return 0;

				default:
					elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
						 fc_typid2);
			}
			break;

		case TIMESTAMPTZOID:
			switch (fc_typid2)
			{
				case DATEOID:
					return -fc_cmpDateToTimestampTz(DatumGetDateADT(fc_val2),
												 DatumGetTimestampTz(fc_val1),
												 fc_useTz);

				case TIMESTAMPOID:
					return -fc_cmpTimestampToTimestampTz(DatumGetTimestamp(fc_val2),
													  DatumGetTimestampTz(fc_val1),
													  fc_useTz);

				case TIMESTAMPTZOID:
					fc_cmpfunc = timestamp_cmp;

					break;

				case TIMEOID:
				case TIMETZOID:
					*fc_cast_error = true; /* 不可比较的类型 */
					return 0;

				default:
					elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
						 fc_typid2);
			}
			break;

		default:
			elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u", fc_typid1);
	}

	if (*fc_cast_error)
		return 0;				/* 转换错误 */

	return DatumGetInt32(DirectFunctionCall2(fc_cmpfunc, fc_val1, fc_val2));
}
